package com.example.easyexcel.imports.customized;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.IgnoreExceptionReadListener;
import com.alibaba.excel.read.metadata.holder.ReadSheetHolder;
import com.alibaba.excel.util.BeanMapUtils;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.ConverterUtils;
import lombok.Getter;
import lombok.SneakyThrows;
import org.springframework.cglib.beans.BeanMap;

import java.lang.reflect.Field;
import java.util.*;

/**
 * Convert to the object the user needs
 *
 *   实现ReadListener监听器，
 *   首先获取excel列对字段的映射，
 *   再重写headMap中每个索引指向的CellData
 *   最后使用easyExcel的ConverterUtils.convertToJavaObject，得到转换后的字段值
 */
public class DynamicImportListener implements IgnoreExceptionReadListener<Map<Integer, ReadCellData<?>>>  {
    @Getter
    private final List dataList ;
    private final Class headClass ;
    private final Map<Integer, Head> headMapNew ;
    private final Map<String, Field> excelColumnName2dtoFieldMap;

    // 初始化
    public DynamicImportListener(Class headClass) {
        this.headClass = headClass;
        this.headMapNew = new HashMap<>();
        this.dataList = new LinkedList();
        // 初始化字段名对应列
        this.excelColumnName2dtoFieldMap = new HashMap<>();
        for (Field declaredField : headClass.getDeclaredFields()) {
            ExcelProperty excelProperty = declaredField.getAnnotation(ExcelProperty.class);
            if(null!=excelProperty ){
                String[] arr = excelProperty.value();
                excelColumnName2dtoFieldMap.put( arr[arr.length-1],declaredField );
            }else{
                excelColumnName2dtoFieldMap.put( declaredField.getName(),declaredField );
            }
        }
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> excelHeadMap, AnalysisContext context) {
        // 生成新的列索引与字段对应关系
        Set<Map.Entry<Integer, ReadCellData<?>>> excelHeadSet = excelHeadMap.entrySet();
        for (Map.Entry<String, Field> entry : excelColumnName2dtoFieldMap.entrySet()) {
            String excelColumnName = entry.getKey();
            Field dtoField = entry.getValue();
            // 通过判断字段上定义的中文名字去匹配解析出来的excel标题,通过equals的方式做精确匹配
            for (Map.Entry<Integer, ReadCellData<?>> excelHeadEntry : excelHeadSet) {
                if (excelHeadEntry.getValue().getStringValue().equals(excelColumnName)) {
                    Integer columnIndex = excelHeadEntry.getKey();
                    headMapNew.put(columnIndex,new Head(columnIndex, dtoField, dtoField.getName(), null, false , false ) );
                    break;
                }
            }
        }
    }

    @Override
    public void invoke(Map<Integer, ReadCellData<?>> cellDataMap, AnalysisContext context) {
        ReadSheetHolder readSheetHolder = context.readSheetHolder();
        Object res = buildUserModel(cellDataMap, readSheetHolder ,context) ;
        context.readRowHolder().setCurrentRowAnalysisResult( res );
    }

    /** 生成数据实体
    * */
    @SneakyThrows
    private Object buildUserModel(Map<Integer, ReadCellData<?>> cellDataMap, ReadSheetHolder readSheetHolder ,
                                  AnalysisContext context) {
        Map<String,Object> map = new HashMap<>();
        BeanMap dataMap = BeanMap.create(BeanMapUtils.create(headClass.newInstance()));
        for (Map.Entry<Integer, Head> entry : headMapNew.entrySet()) {
            Integer index = entry.getKey();
            Head head = entry.getValue();
            String fieldName = head.getFieldName();
            if (cellDataMap.containsKey(index)) {
                ReadCellData<?> cellData = cellDataMap.get(index);
                Object value = ConverterUtils.convertToJavaObject(cellData, head.getField()
                        , ClassUtils.declaredExcelContentProperty(dataMap, headClass, fieldName)
                        , readSheetHolder.converterMap(), context, context.readRowHolder().getRowIndex(), index);
                if (value != null) {
                    //dataMap.put(fieldName, value);
                    map.put(fieldName,value);
                }
            }
        }
        Object res = BeanUtil.toBean(map,headClass);
        dataList.add(res);
        return res;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {}
}
